
#include "taskinfo.h"

using namespace std;

static pid_t get_ppid(const TaskFileContent &t)
{
    istringstream ss(t.status.PPid);
    pid_t res;
    ss >> res;
    return res;
}

static uid_t get_uid(const TaskFileContent &t)
{
    istringstream ss(t.status.Uid);
    uid_t uid;
    ss >> uid;
    return uid;
}

static TaskState get_taskstate(const TaskFileContent &t)
{
    char ch = toupper(t.status.State[0]);
    switch (ch) {
    case 'R':
        return TaskState::R;
    case 'S':
        return TaskState::S;
    case 'D':
        return TaskState::D;
    case 'Z':
        return TaskState::Z;
    case 'T':
        return TaskState::T;
    default:
        return TaskState::UNSUPPORT;
    }
}

static uintptr_t to_vm_byte_value(const string &s)
{
    static int pagesize = getpagesize();

    istringstream ss(s);
    uintptr_t res;
    ss >> res;
    return res * pagesize;
}

static jiffies_t to_jiffies(const string &s)
{
    istringstream ss(s);
    jiffies_t res;
    ss >> res;
    return res;
}

static time_t get_start_time(const TaskFileContent &t)
{
    jiffies_t start_time_ji = to_jiffies(t.stat.start_time);
    time_t start_time = start_time_ji / HZ;
    return SYS.getBootTime() + start_time;
}

static int to_nice(const string &s)
{
    istringstream ss(s);
    int res;
    ss >> res;
    return res;
}

void TaskInfo::loadFromTaskFileContent(const TaskFileContent &content)
{
    this->jiffies = SYS.getTotalCpuTime();
    this->utime   = to_jiffies(content.stat.utime);
    this->stime   = to_jiffies(content.stat.stime);
    this->cutime  = to_jiffies(content.stat.cutime);
    this->cstime  = to_jiffies(content.stat.cstime);

    this->ppid      = get_ppid(content);
    this->name      = content.status.Name;
    this->uid       = get_uid(content);
    this->state     = get_taskstate(content);

    this->vm_size     = to_vm_byte_value(content.statm.size);
    this->vm_resident = to_vm_byte_value(content.statm.resident);
    this->vm_shared   = to_vm_byte_value(content.statm.shared);
    this->vm_private  = this->vm_resident - this->vm_shared;


    this->cpu_time  = utime + stime + cutime + cstime;

    this->start_time = get_start_time(content);
    this->nice       = to_nice(content.stat.nice);
    this->cmd       = content.cmdline.cmdline;

    // 初始化为从进程启动时开始算的平均使用率
    // 如果统一初始化为某个相同的值（如-1或0），启动时相应列的值会同时变化，用户体验很差
    this->cpu_usage = double(cpu_time) / double(jiffies);
}

void TaskInfo::loadFromSys()
{
    TaskFileContent content;
    content.loadFromSys(pid);
    loadFromTaskFileContent(content);
}

TaskInfo::TaskInfo(pid_t pid)
    : pid(pid)
{
    loadFromSys();
    old_ppid = ppid;
}

void TaskInfo::flush()
{
    jiffies_t old_jiffies  = jiffies;
    jiffies_t old_cpu_time = cpu_time;

    old_ppid = ppid;

    loadFromSys();

    cpu_usage = double(cpu_time - old_cpu_time) / double(jiffies - old_jiffies);
}

struct TaskInfoProps
{
    vector<TIP> values;

    map<TIP, int> indexs;

    TaskInfoProps()
    {
        values.push_back(TIP::PID);
        values.push_back(TIP::NAME);
        values.push_back(TIP::PPID);

        values.push_back(TIP::USERNAME);
        values.push_back(TIP::STATE);

        values.push_back(TIP::VM_SIZE);
        values.push_back(TIP::VM_RESIDENT);
        values.push_back(TIP::VM_SHARED);
        values.push_back(TIP::VM_PRIVATE);

        values.push_back(TIP::CPU_USAGE);
        values.push_back(TIP::CPU_TIME);

        values.push_back(TIP::START_TIME);
        values.push_back(TIP::NICE);
        values.push_back(TIP::CMD);

        vector<TIP>(values).swap(values);


        vector<TIP>::size_type i, len = values.size();
        for (i = 0; i != len; ++i) {
            indexs[values[i]] = i;
        }

    }

};

static TaskInfoProps task_info_props;

int TIPUtils::getPropCount()
{
    return task_info_props.values.size();
}

TIP TIPUtils::getPropAt(int index)
{
    if (index >= 0 && index < int(task_info_props.values.size())) {
        return task_info_props.values.at(index);
    }
    return TIP::UNSUPPORT;
}

int TIPUtils::getPropIndex(TIP prop)
{
    return task_info_props.indexs[prop];
}

SortBy TIPUtils::propToSortBy(TIP prop)
{
    switch (prop) {
    case TIP::PID: return SortBy::PID;
    case TIP::PPID: return SortBy::PPID;
    case TIP::NAME: return SortBy::NAME;
    case TIP::USERNAME: return SortBy::USERNAME;
    case TIP::STATE: return SortBy::STATE;
    case TIP::VM_SIZE: return SortBy::VM_SIZE;
    case TIP::VM_RESIDENT: return SortBy::VM_RESIDENT;
    case TIP::VM_SHARED: return SortBy::VM_SHARED;
    case TIP::VM_PRIVATE: return SortBy::VM_PRIVATE;
    case TIP::CPU_USAGE: return SortBy::CPU_USAGE;
    case TIP::CPU_TIME: return SortBy::CPU_TIME;
    case TIP::START_TIME: return SortBy::START_TIME;
    case TIP::NICE: return SortBy::NICE;
    case TIP::CMD: return SortBy::CMD;
    default: return SortBy::PID;
    }
}

SortBy TIPUtils::columnToSortBy(int column)
{
    return propToSortBy(getPropAt(column));
}
